import Card, {
	CardList
} from "./Card";
const module = {};

class WVMap extends Map {
	set(k, v) {
		if (k === 14 || k === 1) {
			super.set(1, v)
			super.set(14, v)
		} else {
			super.set(k, v)
		}
	}
	delete(k) {
		if (k === 14 || k === 1) {
			super.delete(1)
			super.delete(14)
		} else {
			super.delete(k)
		}
	}
}

function get_straight_from_list(list, sort_type = 1, res_len = 5) {
	const res = [];
	list.sort((a, b) => (b - a) * sort_type); // 默认从大到小排序
	const list_len = list.length;
	if (list_len > res_len) {
		for (let i = 0, max_i = list_len - res_len; i <= max_i; i += 1) {
			if (
				list
				.slice(i, res_len - 1 + i)
				.every((item, _i) =>
					item - list[_i + 1 + i] === sort_type
				)
			) {
				return list.slice(i, res_len + i);
			}
		}
	} else if (list_len === res_len && list.slice(0, res_len - 1).every((item, i) => item - list[i + 1] === sort_type)) {
		return list.slice();
	} else {
		return false;
	}
}

function checkSameGenerator(sortAndValueOfList, same_len = 5) {
	return (card_list, s_len = same_len) => {
		const card_num_list = sortAndValueOfList(card_list);
		const res_list = [];
		const total_len = card_num_list.length;
		for (let i = 0, jump_i, len = total_len - s_len + 1; i < len; i += jump_i) {
			jump_i = 1;
			let cur_card = card_num_list[i];
			const same_cards = [cur_card];
			for (let j = i + 1; j < total_len; j += 1) {
				const next_card = card_num_list[j];
				if (next_card === cur_card) {
					same_cards.push(cur_card);
					jump_i += 1;
				} else {
					break;
				}
			}

			const same_cards_len = same_cards.length;
			if (same_cards_len === s_len) {
				res_list.push(card_list.slice(i, i + s_len));
			} else if (same_cards_len > s_len) {
				getListComIndexs(same_cards.length, s_len).forEach(indexs => {
					const res = [];
					for (var _i = 0, len = indexs.length; _i < len; _i++) {
						res[_i] = card_list[indexs[_i] + i]
					}
					res_list.push(res);
				});
			}
		}
		return res_list;
	}
};
// 获取一组数组的组合可能，返回数组索引表
function getListComIndexs(list_len, s_len) {
	const res_list = [];
	const s_len_1 = s_len - 1;

	const indexs = Array(s_len).fill(0);
	const max_index = list_len - s_len;
	do {
		// 把当前位的组合给整理出来
		const cur_index = indexs[s_len_1];
		for (var i = cur_index; i <= max_index; i += 1) {
			indexs[s_len_1] = i;
			const res_indexs = [];
			for (var j = 0; j < s_len; j += 1) {
				res_indexs[j] = indexs[j] + j;
			}
			res_list.push(res_indexs);
		}
		indexs[s_len_1 - 1]++; // 进位
		var keep_loop = fixIndexs(indexs, max_index);
		// 进位修正
		for (var z = 0, z_len = s_len - 1; z < z_len; z += 1) {
			var cur_index_val = indexs[z];
			if (cur_index_val > indexs[z + 1]) {
				indexs[z + 1] = cur_index_val;
			}
		}
		indexs[z] = cur_index_val;
	} while (keep_loop)

	return res_list;
};

function fixIndexs(indexs, max_index) {
	const decimal = max_index + 1;
	for (var i = indexs.length - 1; i >= 0; i -= 1) {
		const cur_index = indexs[i];
		if (cur_index > max_index) {
			if (i === 0) {
				return false
			}
			indexs[i] %= decimal;
			indexs[i - 1] += (cur_index / decimal) | 0;
		}
	}
	return true
};

const check_straight_number = (card_list, s_len = 5) => {
	const card_num_list = card_list.sort((a, b) => a - b);
	const res_list = [];
	for (let i = 0, len = card_num_list.length - s_len + 1; i < len; i += 1) {
		let cur_card = card_num_list[i];
		const straight_cards = [cur_card];
		for (let j = 1; j < s_len; j += 1) {
			const next_card = card_num_list[i + j];
			if (next_card === cur_card + 1) {
				cur_card = next_card;
				straight_cards.push(cur_card);
			} else {
				break;
			}
		}

		if (straight_cards.length === s_len) {
			res_list.push(card_list.slice(i, i + s_len));
		}
	}
	return res_list;
};
const rule = module.exports = {
	getListComIndexs: getListComIndexs,
	// 顺子
	check_straight(card_list, s_len = 5) {
		// TODO:10 J Q K A
		const val_cards_map = new Map();
		const number_list = [];
		card_list.forEach(card => {
			const card_val = +card;
			let same_val_card_list = val_cards_map.get(card_val);
			if (!same_val_card_list) {
				same_val_card_list = [];
				val_cards_map.set(card_val, same_val_card_list);
				number_list.push(card_val);
			}
			same_val_card_list.push(card);
		});
		const straight_numbers_list = check_straight_number(number_list, s_len);
		// 特殊检查 A顺：[10 J Q K A]
		if (val_cards_map.has(1)) {
			let can_A_straight = true;
			for (let i = 10; i <= 13; i += 1) {
				if (!val_cards_map.has(i)) {
					can_A_straight = false;
					break
				}
			}
			if (can_A_straight) {
				straight_numbers_list.push([10, 11, 12, 13, 1]);
			}
		}
		const res_list = [];
		straight_numbers_list.forEach(straight => {
			const indexs = new Array(s_len).fill(0); // 指向组合用的指针
			const lists = straight.map(val => val_cards_map.get(val));
			let loop = true;
			while (loop) {
				const straight_cards = [];
				for (var i = 0; i < s_len; i += 1) {
					straight_cards.push(lists[i][indexs[i]]);
				}
				res_list.push(straight_cards);
				// 移动指针到下一种组合的情况
				for (var i = 0; i < s_len; i += 1) {
					const max_index = lists[i].length - 1;
					const list_index = indexs[i];
					if (list_index < max_index) { // 增1
						indexs[i] += 1;
						break;
					} else if (i < s_len - 1) { // 进位
						indexs[i] = 0;
					} else { // 无法进位
						loop = false;
					}
				}
			}
		});
		return res_list;
	},
	// 同花
	check_flush: checkSameGenerator(card_list => {
		card_list.sort((a, b) => a.valueOfType() - b.valueOfType());
		return card_list.map(card => card.valueOfType());
	}, 5),
	// 同花顺
	check_straight_flush(card_list, s_len = 5) {
		const flush_list = rule.check_flush(card_list);
		const res_list = [];
		flush_list.forEach(card_list => {
			const straight_list = rule.check_straight(card_list);
			res_list.push(...straight_list);
		});
		return res_list;
	},
	// 四条（铁扇，铁支）
	check_four: checkSameGenerator(card_list => {
		return CardList.sort_cards(card_list)
			.map(card => +card)
	}, 4),
	// 三条
	check_three: checkSameGenerator(card_list => {
		return CardList.sort_cards(card_list)
			.map(card => +card)
	}, 3),
	// 对子
	check_two: checkSameGenerator(card_list => {
		return CardList.sort_cards(card_list)
			.map(card => +card)
	}, 2),
	// 两对
	check_two_pair: (card_list) => {
		const two_cards_list = rule.check_two(card_list);
		const res_list = [];
		if (two_cards_list.length) {
			for (let i = 0, i_len = two_cards_list.length - 1; i < i_len; i += 1) {
				const two_cards_1 = two_cards_list[i];
				const set = new Set(two_cards_1);
				for (let j = i_len; j > i; j -= 1) { // 这里用倒序可以提升效率
					const two_cards_2 = two_cards_list[j];
					if (set.has(two_cards_2[0]) || set.has(two_cards_2[1])) {
						break;
					}
					res_list.push([...two_cards_1, ...two_cards_2]);
				}
			}
		}
		return res_list;
	},
	// 葫芦
	check_full_house(card_list) {
		const four_list = rule.check_four(card_list);
		const three_list = [];
		const two_list = [];
		const res_list = [];
		if (four_list.length) {
			// 如果有四条，四条既可以当成三条也可以当成两对
			four_list.forEach(four_cards => {
				three_list.push(...getListComIndexs(four_cards.length, 3)
					.map(indexs =>
						indexs.map(index => four_cards[index])
					)
				);
				two_list.push(...getListComIndexs(four_cards.length, 2)
					.map(indexs =>
						indexs.map(index => four_cards[index])
					)
				);
			});
		}
		// 移除四条
		const less_four_card_list = CardList.filter_remove_cards(card_list,
			four_list.reduce(
				(res_list, card_list) => res_list.concat(card_list), []
			)
		);
		const last_three_list = rule.check_three(less_four_card_list);
		if (last_three_list.length) {
			three_list.push(...last_three_list);
			// 如果有三条，也可以当成两对
			last_three_list.forEach(three_cards => {
				two_list.push(...getListComIndexs(three_cards.length, 2)
					.map(indexs =>
						indexs.map(index => three_cards[index])
					)
				)
			});
		} else if (!three_list.length) {
			// 如果没有任何三条，就没葫芦了
			return res_list;
		}

		const last_two_list = rule.check_two(
			// 排除指定元素
			CardList.filter_remove_cards(less_four_card_list,
				last_three_list.reduce(
					(res_list, card_list) => res_list.concat(card_list), []
				)
			)
		);
		if (last_two_list.length) {
			two_list.push(...last_two_list);
		} else if (!two_list.length) {
			// 如果没有任何两对，就没葫芦了
			return res_list;
		}
		// 进行组合
		three_list.forEach(three_cards => two_list.forEach(two_cards => {
			if (+three_cards[0] !== +two_cards[0]) {
				res_list.push(three_cards.concat(two_cards));
			}
		}));
		return res_list;
	},

	/*报道牌检测*/
	// 至尊青龙,同色一条龙 +52
	is_king_long(card_list, res_info) {
		const one_long_res_info = res_info && {};
		if (card_list.length === 13 && rule.is_one_long(card_list, one_long_res_info)) {
			const color_type = card_list[0].valueOfType();
			if (card_list.every(card => card.valueOfType() === color_type)) {
				if (res_info) {
					res_info.combination = one_long_res_info.combination;
				}
				return true;
			}
			return false;
		}
		return false;
	},
	// 一条龙 +26
	is_one_long(card_list, res_info) {
		if (card_list.length === 13) {
			card_list.sort((a, b) => a.weight - b.weight); //2~14
			if (card_list.every((card, index) => card.weight === index + 2)) {
				if (res_info) {
					res_info.combination = [{
						top: card_list.slice(0, 3),
						mid: card_list.slice(3, 8),
						btm: card_list.slice(8, 13),
					}]
				}
				return true;
			}
			return false;
		}
		return false;
	},
	// 一条龙后再检测是否至尊青龙,同色一条龙 +52
	_is_one_long_and_king(card_list) {
		const color_type = card_list[0].valueOfType();
		if (card_list.every(card => card.valueOfType() === color_type)) {
			return true;
		}
		return false;
	},
	// 六对半 +6
	is_six_pair(card_list) {
		if (card_list.length === 13) {
			const card_val_map = new Map();
			let pair_acc = 0;
			card_list.forEach(card => {
				const card_val = +card;
				const acc = (card_val_map.get(card_val) | 0) + 1;
				card_val_map.set(card_val, acc);
				if (acc === 2) {
					// 一个对子
					pair_acc += 1;
				}
			});
			// 六对半报道 不能有炸弹和同花顺，这两个检测比较消耗时间，所以放置到后面
			return pair_acc === 6 &&
				rule.check_four(card_list).length === 0 &&
				rule.check_straight_flush(card_list).length === 0;
		}
		return false;
	},
	// 三顺子 +6
	check_three_straight(card_list) {
		const res_list = [];
		// 报道 不能有炸弹和同花顺
		if (card_list.length === 13 &&
			rule.check_four(card_list).length === 0 &&
			rule.check_straight_flush(card_list).length === 0
		) {
			[1, -1].some(sort_method => {
				const MID_KEY = sort_method === 1 ? "mid" : "btm";
				const BTM_KEY = sort_method === 1 ? "btm" : "mid";
				// 先取两个五顺，再取一个三顺
				// 两种排序方式，一种是根据权值排序，234~JQKA，一种是根据数值排序，A23~JQK
				// WVMap提供了这两种排序方式的混合体，就是A2~KA
				// 不过不同的是，需要进行两次判定方案，就是从小到大（A2345）与从大到小（AKQJT）获取顺子

				//权值排序
				const weight_card_map = new WVMap();
				card_list.forEach(card => {
					const card_weight = card.weight;
					if (weight_card_map.has(card_weight)) {
						weight_card_map.get(card_weight).push(card)
					} else {
						weight_card_map.set(card_weight, [card]);
					}
				});
				// const is_mixed = weight_card_map.get(1).length > 1;
				const res_cards = {
					top: null,
					mid: null,
					btm: null,
				};
				const card_weight_list = [...weight_card_map.keys()];
				const card_weight_list_len = card_weight_list.length;
				card_weight_list.sort((a, b) => (b - a) * sort_method);
				if (card_weight_list_len < 5) {
					return res_list;
				}
				//[[_0_type, _0_num], [_1_type, _1_num], [_2_type, _2_num]]
				for (let type_and_num_list of[
						[
							[BTM_KEY, 5],
							[MID_KEY, 5],
							['top', 3]
						], [
							[BTM_KEY, 5],
							['top', 3],
							[MID_KEY, 5]
						], [
							['top', 3],
							[BTM_KEY, 5],
							[MID_KEY, 5]
						],
					]) {
					// 复制map
					const weight_card_map_clone = new WVMap(weight_card_map);
					for (let [card_weight, cards] of weight_card_map_clone.entries()) {
						weight_card_map_clone.set(card_weight, cards.slice());
					}
					const _res_cards = {};
					if (
						type_and_num_list.every(type_and_num => {
							const [type, num] = type_and_num;
							const straight_list = _res_cards[type] = [];
							const _card_weight_list = [...weight_card_map_clone.keys()];

							const _straight_weight_list = get_straight_from_list(_card_weight_list, sort_method, num);
							if (_straight_weight_list) {
								for (let card_weight of _straight_weight_list) {
									const cards = weight_card_map_clone.get(card_weight);
									straight_list.push(cards.pop());
									if (cards.length === 0) {
										weight_card_map_clone.delete(card_weight);
									}
								}
								return true;
							}
						})
					) {
						res_cards.top = _res_cards.top;
						res_cards.mid = _res_cards.mid;
						res_cards.btm = _res_cards.btm;
						break;
					}
				}
				if (
					res_cards.top &&
					res_cards.mid &&
					res_cards.btm
				) {
					res_list.push(res_cards);
					return true;
				}
			});
		}
		return res_list;
	},
	is_three_straight(card_list, res_info) {
		const combination = rule.check_three_straight(card_list, res_info);
		if (combination.length) {
			if (res_info) {
				res_info.combination = combination;
			}
			return true;
		}
		return false;
	},
	// 三同花 +6
	is_three_flush(card_list, res_info) {
		//报道 不能有炸弹和同花顺，但是炸弹有4色，所以就不管（PS：如果有两副牌就要另外算了）
		if (card_list.length === 13 && rule.check_straight_flush(card_list).length === 0) {
			const type_map = new Map();
			for (let card of card_list) {
				const type_val = card.valueOfType();
				if (type_map.has(type_val)) {
					type_map.get(type_val).push(card);
				} else {
					type_map.set(type_val, [card]);
					if (type_map.size > 3) {
						return false;
					}
				}
			}
			if (type_map.size === 1) { // 这个应该是同花一条龙的，可是考虑到可能两副牌，就不考虑这个了
				if (res_info) {
					card_list.sort((a, b) => a.weight - b.weight);
					res_info.combination = [{
						top: card_list.slice(0, 3),
						mid: card_list.slice(3, 8),
						btm: card_list.slice(8, 13),
					}];
				}
				return true
			} else if (type_map.size === 2) {
				const [cards_1, cards_2] = [...type_map.values()].sort((a, b) => a.length - b.length);
				if (cards_1.length === 3) {
					if (res_info) {
						const cards_5_5 = cards_2.sort((a, b) => a.weight - b.weight);
						res_info.combination = [{
							top: cards_1,
							mid: cards_5_5.slice(0, 5),
							btm: cards_5_5.slice(-5)
						}]
					}
					return true
				}
				if (cards_1.length === 5) {
					if (res_info) {
						const cards_3_5 = cards_2.sort((a, b) => a.weight - b.weight);
						const cards_mid_or_btm_1 = cards_1;
						const cards_mid_or_btm_2 = cards_3_5.slice(-5);
						const _cards_info_1 = {};
						const _cards_info_2 = {};
						rule.is_flush(cards_mid_or_btm_1, _cards_info_1);
						rule.is_flush(cards_mid_or_btm_2, _cards_info_2);
						if (_cards_info_1.weights_value > _cards_info_2.weights_value) {
							res_info.combination = [{
								top: cards_3_5.slice(0, 3),
								mid: cards_mid_or_btm_2,
								btm: cards_mid_or_btm_1
							}]
						} else {
							res_info.combination = [{
								top: cards_3_5.slice(0, 3),
								mid: cards_mid_or_btm_1,
								btm: cards_mid_or_btm_2
							}]
						}
					}
					return true
				}
				return false;
			} else if (type_map.size === 3) {
				const [cards_3, cards_5_1, cards_5_2] = [...type_map.values()].sort((cards_a, cards_b) => cards_a.length - cards_b.length);
				if (cards_3.length === 3 && cards_5_1.length === 5) {
					if (res_info) {
						const _cards_info_1 = {};
						const _cards_info_2 = {};
						rule.is_flush(cards_5_1, _cards_info_1);
						rule.is_flush(cards_5_2, _cards_info_2);
						if (_cards_info_1.weights_value > _cards_info_2.weights_value) {
							res_info.combination = [{
								top: cards_3,
								mid: cards_5_2,
								btm: cards_5_1
							}]
						} else {
							res_info.combination = [{
								top: cards_3,
								mid: cards_5_1,
								btm: cards_5_2
							}]
						}
					}
					return true
				}
				return false;
			}
		}
		return false;
	},
	// 全小 +6
	is_all_small(card_list) {
		if (card_list.length === 13 && card_list.every(card => card.weight <= 8)) {
			return rule.check_four(card_list).length === 0 &&
				rule.check_straight_flush(card_list).length === 0
		}
		return false
	},
	// 全大 +6
	is_all_big(card_list) {
		if (card_list.length === 13 && card_list.every(card => card.weight >= 8)) {
			return rule.check_four(card_list).length === 0 &&
				rule.check_straight_flush(card_list).length === 0
		}
		return false
	},
	// 检测是否报道牌
	is_special(card_list, res_info) {
		if (card_list.length === 13) {
			// 一条龙或者青龙
			if (rule.is_one_long(card_list, res_info)) {
				if (rule._is_one_long_and_king(card_list)) {
					res_info.type = "king_long";
					return true;
				}
				res_info.type = "one_long";
				return true;
			}
			// 全大 全小 六对半 三同花
			for (let type of["all_big", "all_small", "six_pair", "three_flush", "three_straight"]) {
				if (rule["is_" + type](card_list, res_info)) {
					res_info.type = type;
					return true;
				}
			}
		}
		return false
	},
	// 是否同花顺
	is_straight_flush(card_list, res_info) {
		if (
			card_list.length === 5 &&
			rule.check_flush(card_list).length === 1 &&
			rule.check_straight(card_list).length === 1
		) {
			if (res_info) {
				// 有A的情况，把A当成100，因为存在12345是第二大的顺子，所以不能把A当成11
				res_info.weights_value = card_list.reduce((pre_val, card) => {
					const card_val = +card;
					if (card_val === 1) { // A
						pre_val += 100
					} else {
						pre_val += card
					}
					return pre_val;
				}, 0);
			}
			return true
		}
		return false
	},
	// 是否铁支
	is_four(card_list, res_info) {
		const four_cards_list = rule.check_four(card_list);
		if (
			card_list.length === 5 &&
			four_cards_list.length === 1
		) {
			if (res_info) {
				const four_cards = four_cards_list[0];
				const four_card = four_cards[0];
				const rest_card = CardList.filter_remove_cards(card_list, four_cards)[0];

				res_info.weights_value = four_card.weight * 100 + rest_card.weight;
			}
			return true;
		}
		return false;
	},
	// 是否葫芦
	is_full_house(card_list, res_info) {
		const full_house_cards_list = rule.check_full_house(card_list);
		if (
			card_list.length === 5 &&
			full_house_cards_list.length === 1
		) {
			if (res_info) {
				const full_house_cards = full_house_cards_list[0];
				const card_weight_map = new Map();
				full_house_cards.forEach(card => {
					const card_weight = card.weight;
					card_weight_map.set(card_weight, (card_weight_map.get(card_weight) | 0) + 1);
				});

				let weights_value = 0;
				for (let item of card_weight_map.entries()) {
					const [card_weight, times] = item;
					if (times === 3) {
						weights_value += card_weight * 100;
					} else {
						weights_value += card_weight
					}
				}

				res_info.weights_value = weights_value;
			}
			return true;
		}
		return false;
	},
	// 是否同花
	is_flush(card_list, res_info) {
		if (card_list.length === 5 &&
			rule.check_flush(card_list).length === 1) {
			if (res_info) {
				res_info.weights_value = card_list
					.sort((a, b) => a.weight - b.weight) // 小到大排序
					.reduce((pre_val, card, i) => pre_val + card.weight * Math.pow(10, i), 0);
			}
			return true;
		}
		return false;
	},
	// 是否顺子
	is_straight(card_list, res_info) {
		if (card_list.length === 5 &&
			rule.check_straight(card_list).length === 1) {
			if (res_info) {
				// 有A的情况，把A当成100，因为存在12345是第二大的顺子，所以不能把A当成11
				res_info.weights_value = card_list.reduce((pre_val, card) => {
					const card_val = +card;
					if (card_val === 1) { // A
						pre_val += 100
					} else {
						pre_val += card
					}
					return pre_val;
				}, 0);
			}
			return true;
		}
		return false;
	},
	// 是否三条
	is_three(card_list, res_info = null, len = 5) {
		const three_cards_list = rule.check_three(card_list);
		if (card_list.length === len &&
			three_cards_list.length === 1) {
			if (res_info) {
				const three_cards = three_cards_list[0];
				three_card = three_cards[0];
				const rest_cards = CardList.filter_remove_cards(card_list, three_cards).sort((a, b) => b.weight - a.weight);
				res_info.weights_value = three_card.weight * 100 + rest_cards.reduce((pre_val, card, i) => {
					const pow = Math.pow(10, len - 4 - i);
					return card.weight * pow + pre_val;
				}, 0);
				// rest_cards[0].weight * 10 + rest_cards[1].weight;
			}
			return true;
		}
		return false;
	},
	// 是否两对
	is_two_pair(card_list, res_info) {
		const two_pair_cards_list = rule.check_two_pair(card_list);
		if (card_list.length === 5 &&
			two_pair_cards_list.length === 1) {
			if (res_info) {
				const [two_pair_cards] = two_pair_cards_list;
				const rest_card = CardList.filter_remove_cards(card_list, two_pair_cards)[0];
				const [two_card_1, two_card_2] = [two_pair_cards[0], two_pair_cards[2]].sort((a, b) => b.weight - a.weight);
				res_info.weights_value = two_card_1.weight * 1000 + two_card_2.weight * 100 + rest_card.weight;
			}
			return true;
		}
		return false;
	},
	// 是否对子
	is_two(card_list, res_info = null, len = 5) {
		const two_cards_list = rule.check_two(card_list);
		if (card_list.length === len &&
			two_cards_list.length === 1) {
			if (res_info) {
				const two_cards = two_cards_list[0];
				two_card = two_cards[0];
				const rest_cards = CardList.filter_remove_cards(card_list, two_cards).sort((a, b) => b.weight - a.weight);
				res_info.weights_value = two_card.weight * 1000 + rest_cards.reduce((pre_val, card, i) => {
					const pow = Math.pow(10, len - 3 - i);
					return card.weight * pow + pre_val;
				}, 0);

			}
			return true;
		}
		return false;
	},
	is_any(card_list, res_info = null, len = 5) {
		if (card_list.length === len) {
			if (res_info) {
				card_list.sort((a, b) => b.weight - a.weight);
				res_info.weights_value = card_list.reduce((pre_val, card, i) => {
					const pow = Math.pow(10, 5 - i); // 一定要以5开始这要三张牌和5张牌才能对比出倒水情况
					return card.weight * pow + pre_val;
				}, 0);
			}
			return true;
		}
		return false;
	}
};
export default rule;